<?php

/**
 * @file
 * Strategy with access behaviour
 *
 * @usage
 * $context = new HeartbeatMessageBuilder(new PublicBehaviour());
 * $context->execute();
 *
 * $context = new HeartbeatMessageBuilder(new PrivateBehaviour());
 * $context->execute();
 *
 * $context = new HeartbeatMessageBuilder(new ConnectedBehaviour());
 * $context->execute();
 *
 */

/**
 * Class HeartbeatMessageBuilder
 * Message builder that fetches and customizes a
 * users activity on the site.
 * The builder takes a heartbeataccess state object to
 * handle the request to fetch the messages.
 *
 */
class HeartbeatMessageBuilder {

  private $_heartbeatState = null;
  private $_hasErrors = FALSE;
  private $_errors = array();

  public function __construct(HeartbeatAccess $heartbeatState) {

    if (!$heartbeatState->hasErrors()) {
      $this->_heartbeatState = $heartbeatState;
    }
    else {
      $this->setError($heartbeatState->getErrors());
    }
  }

  public function hasErrors() {
    return $this->_hasErrors;
  }

  protected function setError($errors) {
    $this->_errors = $errors;
    $this->_hasErrors = TRUE;
  }

  public function getErrors() {
    return $this->_errors;
  }

  public function getState() {
    return $this->_heartbeatState;
  }

  public function updateState($heartbeatAccess) {
    $this->_heartbeatState = $heartbeatAccess;
  }

  public function execute() {

    $heartbeat = $this->_heartbeatState->fetchMessages();

    if (!empty($heartbeat->raw_messages)) {

      $this->checkAccess($heartbeat);

      return $this->group($heartbeat);
    }

    return array();
  }

  /**
   * Function to check access on messages
   * This behaviour is set by a heartbeat message configuration
   * to overrule the chosen display access type
   */
  private function checkAccess(HeartbeatParser $heartbeat) {

    global $user;
    $stream = $this->_heartbeatState->getStream();
    $tags = $heartbeat->get_tags();

    // First check access on message perms, roles and node access
    foreach ($heartbeat->raw_messages as $key => $message) {

      // Check on xss attack before other modules can change messages.
      $heartbeat->raw_messages[$key]->message = filter_xss($heartbeat->raw_messages[$key]->message, $tags);
      $heartbeat->raw_messages[$key]->message_concat = filter_xss($heartbeat->raw_messages[$key]->message_concat, $tags);

      // unset message types that where denied in stream access configuration
      if (isset($stream->messages_denied[$message->message_id])) {
        unset($heartbeat->raw_messages[$key]);
      }

      // unset the private messages when showing a display to others
      if ($message->perms == HEARTBEAT_PRIVATE && !($this->_heartbeatState instanceOf PrivateHeartbeat)) {
        unset($heartbeat->raw_messages[$key]);
      }

      // Check the roles for this type of message
      if (!isset($message->roles[DRUPAL_ANONYMOUS_RID])) {
        $access = FALSE;
        foreach ($message->roles as $rid) {
          if (isset($user->roles[$rid])) {
            $access = TRUE;
            break;
          }
        }
        if (!$access) {
          unset($heartbeat->raw_messages[$key]);
        }
      }

      // Check if the current user has access to nodes and user profiles
      if (!user_access('access user profiles', $user)) {
        $heartbeat->raw_messages[$key]->uid_access = FALSE;
      }
      if ($message->nid > 0) {

        if (!node_access('view', node_load($message->nid), $user)) {
          $heartbeat->raw_messages[$key]->nid_access = FALSE;
        }

        if ($message->nid_target > 0) {
          if (!node_access('view', node_load($message->nid_target), $user)) {
            $heartbeat->raw_messages[$key]->nid_target_access = FALSE;
          }
        }
      }
    }

    // Check if any filters are active giving other modules the chance
    // to list the messages that are evaluated by this filter
    if (isset($_GET['filters'])) {

      $active_filters = drupal_map_assoc(explode(',', $_GET['filters']));
      $filtered_messages = array();

      foreach ($active_filters as $filter) {

        $function = 'heartbeat_filter_' . str_replace('-', '_', $filter);
        if (function_exists($function)) {
          $filtered_messages += $function($heartbeat->raw_messages, $this->_heartbeatState);
        }
      }
      $heartbeat->raw_messages = $filtered_messages;
    }

    // Give other modules the oportunity to remove messages
    // based on their own parameters and reasons.
    $hook = 'heartbeat_messages_alter';
    foreach (module_implements($hook) as $module) {
      $function = $module .'_'. $hook;
      $result = $function($heartbeat->raw_messages, $this->_heartbeatState);
    }

  }

  /**
   * Function to check if more/older messages can be loaded
   *
   * @return Boolean has more messages
   */
  public function hasMoreMessages($page = TRUE) {

    $stream = $this->_heartbeatState->getStream();
    if ($page) {
      $display_pager = $stream->page_show_pager;
    }
    else {
      $display_pager = $stream->block_show_pager;
    }

    return $stream->pager && $display_pager;
  }

  /**
   * @see class HeartbeatParser
   */
  public function group(HeartbeatParser $heartbeat) {

    $heartbeat->set_timespan_gap(variable_get('heartbeat_activity_grouping_seconds', 720));
    $heartbeat->build_sets($heartbeat->raw_messages);
    $heartbeat->merge_sets();
    $messages = $heartbeat->get_messages();

    // $messages = $heartbeat->remove_broken_messages();
    $num_total_messages = count($messages);

    // From here we know the number of messages actualy loaded (and allowed)
    $messages = array_slice($messages, 0, $this->_heartbeatState->stream->limit_sql);

    // Set the possibility of a pager appearence
    if ($num_total_messages > $this->_heartbeatState->stream->limit_sql) {
      $this->_heartbeatState->stream->pager = TRUE;
    }

    // Give contributes modules the change to retheme or rebuild messages
    $hook = 'heartbeat_theme_alter';
    foreach (module_implements($hook) as $module) {
      $function = $module .'_'. $hook;
      $result = $function($messages, $this->_heartbeatState);
    }

    return $messages;
  }
}